package com.hr;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Set;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.espirit.eap.manager.DepartmentService;
import com.espirit.eap.sa.Employee;
import com.espirit.eap.sa.UserService;
import com.espirit.finance.core.AcctTerm;
import com.espirit.finance.core.TicketAware;
import com.espirit.finance.core.Voucher;
import com.espirit.finance.core.VoucherServiceSupport;
import com.googlecode.cswish.annotation.ParamScope;
import com.googlecode.cswish.exception.ApplicationException;
import com.googlecode.cswish.model.PageElements;
import com.googlecode.cswish.model.PageInfo;
import com.googlecode.cswish.model.ParameterMap;
import com.googlecode.cswish.model.Reference;
import com.googlecode.cswish.oosearch.Condition;
import com.googlecode.cswish.oosearch.QLInfo;
import com.googlecode.cswish.struts.spring.GenericService;
import com.googlecode.cswish.struts.spring.SearchCallback;
import com.googlecode.cswish.util.bean.ModelUtil;

@Service
public class SalaryService extends VoucherServiceSupport{
	private static final Log logger = LogFactory
			.getLog(SalaryService.class);

	@Resource
	GenericService genericService;
	
	@Resource
	DepartmentService departmentService;
	
	@Resource
	HrSettingService hrSettingService;
	
	@Resource
	SalaryTicketService salaryTicketService;
	
	@Resource
	SalaryAcctItemService salaryAcctItemService;
	
	
	@Resource
	UserService userService;
	
	protected EntityManager em;
	
	@PersistenceContext(unitName="espirit")
	public void setEntityManager(EntityManager em) {
		this.em = em;
	}
	
	public  PageInfo search(final ParameterMap model, int start, int limit,
    		String sort, String dir, boolean newSearch, Reference<QLInfo> qlInfoRef,
    		@ParamScope(visible=false, value="#ps")PageElements ps ,boolean fromMenu
    		) {
		if (model != null && model.getParameter("employee.id") != null){
			ps.setElementVisible("model.employee.id", false);
		} else {
			ps.setElementVisible("model.employee.id", true);
		}
		SearchCallback callback = departmentService.buildDeptDefaultSearchCallback(model, null, null);
		return genericService.search(model, start, limit, sort, dir, newSearch, qlInfoRef, callback, true, false);
	}
	
	
	@Transactional
	public  void add(TicketAware ticketAware){
		super.add(ticketAware);
	}
	
	public void _update(Salary model, @ParamScope(visible=false, value="#ps")PageElements ps){
		ps.setElementReadOnly("model.acctTerm.id", true);
		ps.setElementReadOnly("model.department.id", true);
		ps.setElementReadOnly("model.employee.id", true);
		ps.setElementReadOnly("model.amount", true);
		genericService.view(model);
	}
	
	public void _reCalcSalary(Salary model, @ParamScope(visible=false, value="#ps")PageElements ps){
		ps.setElementReadOnly("model.acctTerm.id", true);
		ps.setElementReadOnly("model.state", true);
		
		ps.setElementRequired("model.department.id", true);
		ps.setElementRequired("model.deptIds", true);
		ps.setElementRequired("model.employee.id", false);
		model.setAcctTerm(hrSettingService.getHrSetting().getCurrentAcctTerm());
		model.setState(Salary.STATE_NORMAL);
	}
	
//	@Transactional
//	@Async
	public synchronized void reCalcSalary(Salary model){
		String empId = null;
		if (model.getEmployee() != null && model.getEmployee().getId() != null){
			empId = model.getEmployee().getId();
		}
		String deptIds = model.getDeptIds();
		String[] ids = null;
		if (!StringUtils.isEmpty(deptIds)){
			ids = deptIds.split(",");
		}
		Set<String> empIds = departmentService.getAllValidEmployeeIds(ids, empId);
		int itemCount = 0;
		for(String id : empIds){
			SalaryTicket salaryTicket = salaryTicketService.getCurrentSalaryTicket(id);
			if (salaryTicket == null){
				Employee emp = genericService.load(Employee.class, id);
				logger.error("员工" + emp.getName() + " 工资凭据为空"  );
			} else {
				add(salaryTicket);
				itemCount ++;
				logger.debug("第：" + itemCount + " 员工： " + salaryTicket.getEmployee().getName() );
			}
//			if (++itemCount % 100 == 0){
//				em.flush();
//				em.clear();
//			}
		}
	}
	
	private void deleteCurrentSalary(String empId){
		//keep use input salaryitem: salaryadjitem;
		Salary salary = getCurrentSalary(empId);
		if (salary == null) return ;
		if (!salary.getState().equals(Salary.STATE_NORMAL)){
			throw new ApplicationException("工资状态不为正常，不能删除");
		}
		
//		genericService.delete(salary);
	}
	
	public Salary getCurrentSalary(String empId) {
		Salary salary = new Salary();
		AcctTerm currTerm = hrSettingService.getHrSetting().getCurrentAcctTerm();
		ModelUtil.setProperty(salary, "employee.id", empId);
		ModelUtil.setProperty(salary, "acctTerm.id", currTerm.getId());
		salary = genericService.searchByModel(salary, Condition.SEARCH_ONE).getData(0);
		return salary;
	}
	
	public void _generateBankCollection(BankCollection model,  @ParamScope(visible=false, value="#ps")PageElements ps){
		ps.setElementVisible("model.ids", false);
		ps.setElementReadOnly("model.acctTerm.id", true);
		ps.setElementReadOnly("model.state", true);
		ps.setElementReadOnly("model.itemCounts", true);
		ps.setElementReadOnly("model.amount", true);
		
		//calc total 
		List<Salary> results = getCurrentBankCollectionSalary();
		if (results == null) return ;
		int itemCounts = 0;
		BigDecimal amount = BigDecimal.ZERO;
		for(Salary salary : results){
			//need check bank number?
			itemCounts ++;
			amount = amount.add(salary.getAmount());
		}
		model.setAcctTerm(hrSettingService.getHrSetting().getCurrentAcctTerm());
		model.setState(Salary.STATE_NORMAL);
		model.setItemCounts(itemCounts);
		model.setAmount(amount);
	}
	private List<Salary> getCurrentBankCollectionSalary(){
		Salary example = new Salary();
		example.setAcctTerm(hrSettingService.getHrSetting().getCurrentAcctTerm());
		example.setState(Salary.STATE_NORMAL);
		List<Salary> results = genericService.searchAll(example).getData();
		return results == null ? Collections.EMPTY_LIST : results;
	}
	
	public void generateBankCollection(BankCollection model){
		if (model == null) return ;
		
		List<Salary> results = getCurrentBankCollectionSalary();
		if (results == null || results.isEmpty()) return ;
		
		BankCollection normalPay = new BankCollection();
		normalPay.setAcctTerm(hrSettingService.getHrSetting().getCurrentAcctTerm());
		normalPay.setState(BankCollection.STATE_NORMAL);
		
		normalPay = genericService.searchAll(normalPay).getData(0);
		if (normalPay != null){
			genericService.delete(normalPay);
		}
		
		model.setAcctTerm(hrSettingService.getHrSetting().getCurrentAcctTerm());
		model.setState(BankCollection.STATE_NORMAL);
		model = genericService.add(model);
		
		for(Salary salary : results){
			salary.setPayModel(Salary.PAY_MODEL_BANKCOLLECTION);
			salary.setState(Salary.STATE_BANKCOLLECTION);
			salary = genericService.merge(salary);
			
			BankCollectionItem child = new BankCollectionItem();
			child.setSalary(salary);
			String bankNbr = salary.getEmployee().getBankNbr();
			if (StringUtils.isEmpty(bankNbr)) {
				throw new ApplicationException("员工银行账号为空", salary.getEmployee().getName());
			}
			child.setBankNbr(bankNbr);
//			model.safeChildren().add(child);
			child.setParent(model);
			genericService.add(child);
		}
		
	}

	@Override
	public void onAdd(Voucher voucher) {
		SalaryItem salary = (SalaryItem) voucher;
		SalaryTicket salaryTicket = (SalaryTicket) voucher.getTicketAware();
		salary.setEmployee(salaryTicket.getEmployee());
		if (salaryTicket.getEmployee() != null){
			salary.setDepartment(salaryTicket.getEmployee().getDepartment());
		}
		salary.setAcctTerm(hrSettingService.getHrSetting().getCurrentAcctTerm());
		salary.setCreated(userService.getLoginUser());
	}

	@Override
	public Class getVoucherClazz() {
		return SalaryItem.class;
	}

	@Override
	public List<SalaryAcctItem> getAcctItems(
			TicketAware ticketAware) {
		SalaryTicket salaryTicket = (SalaryTicket) ticketAware;
		if (salaryTicket.getEmployee() == null) return Collections.EMPTY_LIST;
		String empId = salaryTicket.getEmployee().getId();
		return salaryAcctItemService.getSalaryAcctItems(empId);
	}
	
	@Override
	public void afterAdd(List<Voucher> children, TicketAware ticketAware) {
		SalaryTicket salaryTicket = (SalaryTicket) ticketAware;
		
		Salary salary = getCurrentSalary(salaryTicket.getEmployee().getId());
		if (salary == null){
			salary = new Salary();
			salary.setState(Salary.STATE_NORMAL);
			salary.setAcctTerm(hrSettingService.getHrSetting().getCurrentAcctTerm());
			salary.setEmployee(salaryTicket.getEmployee());
			salary = genericService.add(salary);
			if (salary.getChildren() == null){
				salary.setChildren(new ArrayList<SalaryItemBase>());
			}
		} else {
			if (!salary.getState().equals(Salary.STATE_NORMAL)){
				throw new ApplicationException("工资状态不为正常，不能删除");
			}
			if (salary.getChildren() != null){
				ListIterator<SalaryItemBase> iter = salary.getChildren().listIterator();
				while(iter.hasNext()){
					SalaryItemBase sib = iter.next();
					if (sib instanceof SalaryItem){
						iter.remove();
					}
				}
			}
		}
		
		salary.setCreatedOn(new Date());
		salary.setCreated(userService.getLoginUser());
		
		
		if (children == null || children.isEmpty()) {
			salary.setAmount(null);
			genericService.merge(salary);
			return;
		}
		
		BigDecimal amount = BigDecimal.ZERO;
//		List<SalaryItemBase> salaryItems = new ArrayList<SalaryItemBase>();
		for(Voucher voucher : children){
			SalaryItem salaryItem = (SalaryItem) voucher;
			if (salary.getEmployee() == null){
				salary.setEmployee(salaryItem.getEmployee());
				if (salaryItem.getEmployee() != null){
					salary.setDepartment(salaryItem.getEmployee().getDepartment());
				}
			}
//			if (salaryItem.getAmount() != null){
//				amount = amount.add(salaryItem.getAmount().multiply(BigDecimal.valueOf(salaryItem.getDc())));
//			}
			salaryItem.setParent(salary);
//			salaryItem = genericService.add(salaryItem);
			salary.getChildren().add(salaryItem);
//			salaryItems.add(salaryItem);
		}
//		salary.setChildren(salaryItems);
		for(SalaryItemBase salaryItemBase : salary.getChildren()){
			if (salaryItemBase.getAmount() != null){
				amount = amount.add(salaryItemBase.getAmount().multiply(BigDecimal.valueOf(salaryItemBase.getDc())));
			}
		}
		salary.setAmount(amount);
		salary.setCreated(userService.getLoginUser());
		genericService.merge(salary);
	}
	
	public void reCalEmpSalary(Employee emp){
		if (emp == null) return ;
		Salary example = getCurrentSalary(emp.getId());
		if (example != null){
			Salary model = new Salary();
			model.setEmployee(emp);
			this.reCalcSalary(model);
		}
	}
}
